package com.twoscreen;

import java.util.ArrayList;
import java.util.logging.Logger;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.channel.ChannelMessage;
import com.google.appengine.api.channel.ChannelService;
import com.google.appengine.api.channel.ChannelServiceFactory;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

/**
 * Class provides a communication between the TV and Interactive Device using the Channel open in TokenServlet
 * @author James Smith
 */
public class CommServlet extends HttpServlet {
	private static final Logger log = Logger.getLogger("com.twoscreen.CommServlet.java");
	private static PersistenceManager pm = PMF.get().getPersistenceManager();
	private static final long serialVersionUID = 1L;
	private static ChannelService channelService = ChannelServiceFactory.getChannelService();
	
	
	public void doPost(HttpServletRequest request, HttpServletResponse response) {
		//Check the parameters exist and assign them to variables
		int code = 0;
		String playerScreenId = null;
		String videoId = null;
		int time = -1;
		String viewerId = null;
		
		if (request.getParameter("code") != null) {
			code = Integer.parseInt(request.getParameter("code"));
		}
		if (request.getParameter("psId") != null) {
			playerScreenId = request.getParameter("psId");
		}
		if (request.getParameter("vidId") != null) {
			videoId = request.getParameter("vidId");
		}
		if (request.getParameter("time") != null) {
			time = Integer.parseInt(request.getParameter("time"));
		}
		
		if (code != 1) {
			UserService userService = UserServiceFactory.getUserService();
			User user = null;
			try {
				user = userService.getCurrentUser();
				viewerId = TUser.getTUser(user).getViewerId();
			} catch (Exception e) {
				log.warning("Error getting the user: " + e);
			}
		}
		
		//Get the passed parameters
	    log.info("Message received: psId:" + playerScreenId +
	    		" vidId: " + videoId +
	    		" time: " + time +
	    		" viewer: " + viewerId);
		
		switch (code) {
		case 1:
			//TV sends event message
			if (playerScreenId != null && videoId != null && time > 0) {
				log.info("Sending app event");
		    	sendAppEvent(playerScreenId, videoId, time);
		    } else {
		    	log.warning("Event send failed as not all params passed");
		    }
			break;
			
		case 2:
			//App sends video update message
			if (videoId != null && viewerId != null) {
				log.info("Sending video update");
				sendVideoUpdate(viewerId, videoId);
			} else {
				log.warning("TV update send failed as not all params passed");
			}
			break;
			
		case 3:
			//App sends play/stop message
			if (viewerId != null) {
				log.info("play/stop triggered");
				tvPlay(viewerId);
			}
			break;
		
		default:
			log.warning("no code passed");
			break;
		}
	    
	}
	
	/**
	 * Sends an update channel message to the interactive device to 
	 * load content for the video being played
	 * @param viewerId The viewer to send the message to
	 * @param videoId The video ID the content should be loaded for
	 */
	public static void sendVideoUpdate(String viewerId, String videoId) {
		//Determine which screen to update
			//Try from Cache first
		String psId = DataCache.getStringFromCache(viewerId);
		if (psId == null) {
			psId = DeviceConnect.getDC(viewerId).getPsID();
			log.info("viewer ID not in cache");
		}
		
		//Send the TV update
		String outputMessage = "<data>" +
	    		"<type>updateMessage</type>" +
	    		"<vidId>" + videoId + "</vidId>" +
	    		"</data>";
		CommServlet.sendMessageToChannel(psId, outputMessage);
		log.info("TV message sent to: " + psId + "\n" +
	    		"Message: " + outputMessage);
		
		//Update all the apps
		//Get an array of all connected devices
	    ArrayList<String> viewers = DeviceConnect.getConnections(psId);
	    
	    //Send message to all connected users
	    log.info("Start of app update loop. Number of apps to update: " + viewers.size());
	    for (int i=0 ; i<viewers.size() ; i++) {
	    	String toViewerId = viewers.get(i);
	    	String outputMessage2 = "<data>" +
		    		"<type>updateMessage</type>" +
		    		"<vidId>" + videoId + "</vidId>" +
		    		"</data>";
	    	
		    //Print them to make sure it worked
	    	log.info("Sending app update for: Player Screen ID: " + psId +
		    		" Viewer ID: " + toViewerId +
		    		" Video ID: " + videoId);
		    
		    //Send the string to the app to be displayed
	    	CommServlet.sendMessageToChannel(toViewerId, outputMessage2);
		    log.info("Message sent to: " + toViewerId + "\n" +
		    		"Message: " + outputMessage2);
	    }
	    log.info("End of app update loop");
	}
	
	/**
	 * Sends the interactive device a message triggering an event action
	 * for each interactive device connected to the given player screen ID
	 * @param playerScreenId The player screen that sent the message
	 * @param videoId The video ID to get the action for
	 * @param time The time of the action
	 */
	public static void sendAppEvent(String playerScreenId, String videoId, int time) {
		//Get an array of all connected devices
	    ArrayList<String> viewers = DeviceConnect.getConnections(playerScreenId);
	    String action = null;
	    Key key = KeyFactory.createKey(Event.class.getSimpleName(), videoId+time);
		log.info("Attempting to find the event in the datastore to get the action");
		try {
			action = pm.getObjectById(Event.class, key).getAction();
			log.info("Found action: " + action);
		} catch (NullPointerException e) {
			log.info("action not found for video: " + videoId + " time: " + time + "\nTest failed " + e);
		} catch (JDOObjectNotFoundException e) {
			log.info("action not found for video: " + videoId + " time: " + time + "\nTest failed " + e);
		}
	    
	    //Send message to all connected users
	    for (int i=0 ; i<viewers.size() ; i++) {
	    	String toViewerId = viewers.get(i);
	    	String outputMessage = "<data>" +
		    		"<type>eventMessage</type>" +
		    		"<event>" + action + "</event>" +
		    		"</data>";
	    	
		    //Print them to make sure it worked
	    	log.info("Sending app event for: Player Screen ID: " + playerScreenId +
		    		" Viewer ID: " + toViewerId +
		    		" Video ID: " + videoId +
		    		" Time stamp: " + time);
		    
		    //Send the string to the app to be displayed
	    	CommServlet.sendMessageToChannel(toViewerId, outputMessage);
		    log.info("Message sent to: " + toViewerId + "\n" +
		    		"Message: " + outputMessage);
	    }
	}
	
	/**
	 * Sends Channel message to the designated ID
	 * @param id The viewerId or playerScreenId
	 * @param message The message to be sent
	 */
	public static void sendMessageToChannel(String id,String message){
		channelService.sendMessage(new ChannelMessage(id, message));
	}
	
	/**
	 * Sends a message to the iTV to play or stop the current video
	 * @param viewerId The viewer ID that sent the message
	 */
	public static void tvPlay(String viewerId) {
		//Determine which screen to update
		String psId = DeviceConnect.getDC(viewerId).getPsID();
		String outputMessage = "<data>" +
	    		"<type>play</type>" +
	    		"</data>";
		sendMessageToChannel(psId, outputMessage);
		log.info("message sent to: " + psId + " message: " + outputMessage);
	}
	
}
